package com.gitee.wsl.doc.prosemirror.ui.inputrule

import com.gitee.wsl.doc.prosemirror.state.EditorState
import com.gitee.wsl.doc.prosemirror.state.Transaction
import com.gitee.wsl.doc.prosemirror.ui.inputrule.InputRule.Options

const val MAX_MATCH = 500

typealias PluginState = InputRuleState?

class InputRuleState(
    val transform: Transaction,
    val from: Int,
    val to: Int,
    val text: String
)

fun interface InputRuleHandler{
    fun execute(
        state: EditorState,
        match: MatchResult,
        start: Int,
        end: Int
    ): Transaction?
}

fun InputRuleHandler.toInputRule(
    match: Regex,
    options: Options = Options()
) = InputRule(match = match,this,options)

/// Create an input rule. The rule applies when the user typed
/// something and the text directly in front of the cursor matches
/// `match`, which should end with `$`.
///
/// The `handler` can be a string, in which case the matched text, or
/// the first matched group in the regexp, is replaced by that
/// string.
///
/// Or a it can be a function, which will be called with the match
/// array produced by
/// [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
/// as well as the start and end of the matched range, and which can
/// return a [transaction](#state.Transaction) that describes the
/// rule's effect, or null to indicate the input was not handled.
open class InputRule(
    val match: Regex,
    val handler:InputRuleHandler,
    val options: Options = Options(),
){
    class Options(
        /// When set to false,
        /// [`undoInputRule`](#inputrules.undoInputRule) doesn't work on
        /// this rule.
        val undoable: Boolean = false,
        /// By default, input rules will not apply inside nodes marked
        /// as [code](#model.NodeSpec.code). Set this to true to change
        /// that, or to `"only"` to _only_ match in such nodes.
        val inCode: Boolean = false,
        /// When set to `false`, this rule will not fire inside marks
        /// marked as [code](#model.MarkSpec.code). The default is
        /// `true`.
        val inCodeMark: Boolean = true
    )

    companion object
}

fun InputRule(match: Regex, handler:String, options: InputRule.Options = InputRule.Options())
      = InputRule(match,stringHandler(handler),options)

fun stringHandler(replacement: String): InputRuleHandler {
    return InputRuleHandler{ state, match, start, end ->
        var insert = replacement
        val groups = match.groupValues

        if (groups.size > 1) {
            val matchedText = match.value
            val firstGroup = groups[1]
            val offset = matchedText.lastIndexOf(firstGroup)

            if (offset != -1) {
                insert += matchedText.substring(offset + firstGroup.length)
                val newStart = start + offset
                val cutOff = newStart - end

                if (cutOff > 0) {
                    insert = matchedText.substring(offset - cutOff, offset) + insert
                    state.tr.insertText(insert, end, end)
                } else {
                    state.tr.insertText(insert, newStart, end)
                }
            } else {
                state.tr.insertText(insert, start, end)
            }
        } else {
            state.tr.insertText(insert, start, end)
        }
    }
}